package com.wfh.aigenerator.core;

import cn.hutool.json.JSONUtil;
import com.wfh.aigenerator.ai.AiGenService;
import com.wfh.aigenerator.ai.AiGenServiceFactory;
import com.wfh.aigenerator.ai.model.HtmlCodeResult;
import com.wfh.aigenerator.ai.model.MultiFileCodeResult;
import com.wfh.aigenerator.ai.model.message.AiResponseMessage;
import com.wfh.aigenerator.ai.model.message.ToolExecutedMessage;
import com.wfh.aigenerator.ai.model.message.ToolRequestMessage;
import com.wfh.aigenerator.common.ErrorCode;
import com.wfh.aigenerator.core.parser.CodeParserExector;
import com.wfh.aigenerator.core.saver.CodeFileSaverExector;
import com.wfh.aigenerator.exception.BusinessException;
import com.wfh.aigenerator.model.enums.CodeGenTypeEnums;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.service.TokenStream;
import dev.langchain4j.service.tool.ToolExecution;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;

/**
 * @Title: AiCodeGenFacade
 * @Author wangfenghuan
 * @Package com.wfh.aigenerator.core
 * @Date 2025/7/29 20:58
 * @description: 门面类
 */
@Service
@Slf4j
public class AiCodeGenFacade {


    @Resource
    private AiGenServiceFactory aiGenServiceFactory;

    /**
     * 统一入口，
     * @param userMessage
     * @param codeGenTypeEnums
     * @return
     */
    public File genAndSaveCode(String userMessage, CodeGenTypeEnums codeGenTypeEnums, Long appId){
        if (codeGenTypeEnums == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        AiGenService aiGenService = aiGenServiceFactory.getAiGenService(appId);
        return switch (codeGenTypeEnums) {
            case HTML -> {
                HtmlCodeResult htmlCodeResult = aiGenService.genCodeWithHtml(userMessage);
                yield CodeFileSaverExector.exectorSave(htmlCodeResult, CodeGenTypeEnums.HTML, appId);
            }
            case MULTI_FILE -> {
                MultiFileCodeResult result = aiGenService.genCodeWithMultiFile(userMessage);
                yield CodeFileSaverExector.exectorSave(result, CodeGenTypeEnums.MULTI_FILE, appId);
            }
            default -> {
                String errMsg = "不支持生成的类型" + codeGenTypeEnums.getValue();
                throw new BusinessException(ErrorCode.PARAMS_ERROR, errMsg);
            }
        };
    }

    /**
     * 统一入口，flux
     * @param userMessage
     * @param codeGenTypeEnums
     * @return
     */
    public Flux<String> genAndSaveCodeWithFlux(String userMessage, CodeGenTypeEnums codeGenTypeEnums, Long appId){
        if (codeGenTypeEnums == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        AiGenService aiGenService = aiGenServiceFactory.getAiGenService(appId);
        return switch (codeGenTypeEnums) {
            case HTML -> {
                Flux<String> stringFlux = aiGenService.genCodeWithHtmlStream(userMessage);
                yield processCodeStream(stringFlux, CodeGenTypeEnums.HTML, appId);

            }
            case MULTI_FILE -> {
                Flux<String> stringFlux = aiGenService.genCodeWithMultiFileStream(userMessage);
                yield processCodeStream(stringFlux, CodeGenTypeEnums.MULTI_FILE, appId);
            }
            case VUE -> {
                log.info("【DEBUG】Calling genCodeWithVueStream with appId = {}, type = {}",
                        appId, appId != null ? appId.getClass() : "null");
                TokenStream tokenStream = aiGenService.genCodeWithVueStream(appId, userMessage);
                yield processCodeStream(tokenStream, CodeGenTypeEnums.VUE, appId);
            }
            default -> {
                String errMsg = "不支持生成的类型" + codeGenTypeEnums.getValue();
                throw new BusinessException(ErrorCode.PARAMS_ERROR, errMsg);
            }
        };
    }

    /**
     * 将tokenstream转化为Flux<String>
     * @param tokenStream
     * @param codeGenTypeEnums
     * @param appId
     * @return
     */
    private Flux<String> processCodeStream(TokenStream tokenStream, CodeGenTypeEnums codeGenTypeEnums, Long appId) {

        return Flux.create(sink -> {
            tokenStream.onPartialResponse((String partialResponse) -> {
                // ai的相应
                AiResponseMessage aiResponseMessage = new AiResponseMessage(partialResponse);
                sink.next(JSONUtil.toJsonStr(aiResponseMessage));
            })
                    // 工具调用请求
                    .onPartialToolExecutionRequest((index, toolExecutionRequest) -> {
                        ToolRequestMessage toolRequestMessage = new ToolRequestMessage(toolExecutionRequest);
                        sink.next(JSONUtil.toJsonStr(toolRequestMessage));
                    })
                    // 工具执行的信息
                    .onToolExecuted((ToolExecution toolExecution) -> {
                        ToolExecutedMessage toolExecutedMessage = new ToolExecutedMessage(toolExecution);
                        sink.next(JSONUtil.toJsonStr(toolExecutedMessage));
                    })
                    // 完成
                    .onCompleteResponse((ChatResponse response) -> {
                        sink.complete();
                    })
                    // 如果有错误信息的话，直接拼接上去
                    .onError((Throwable error) -> {
                        error.printStackTrace();
                        sink.error(error);
                    })
                    .start();
        });
    }


    private Flux<String> processCodeStream(Flux<String> codeStream, CodeGenTypeEnums codeGenTypeEnums, Long appId){
        StringBuilder codeBuilder = new StringBuilder();
        // 实时收集代码片段
        return codeStream.doOnNext(codeBuilder::append).doOnComplete(() -> {
            try {
                String compelete = codeBuilder.toString();
                // 使用执行器解析代码
                Object result = CodeParserExector.execParser(compelete, codeGenTypeEnums);
                // 使用执行器保存代码
                File file = CodeFileSaverExector.exectorSave(result, codeGenTypeEnums, appId);
                log.info("保存代码成功，{}", file.getAbsolutePath());
            } catch (Exception e) {
                log.error("保存失败: {}", e.getMessage());
            }
        });
    }
}
